#include "w25qxx.h"
#include "spi.h"
#include "gpio.h"

static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;    
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

/**
  * @brief  W25QXX SPI 初始化
  * @param  无
  * @retval 无
  */
void W25QXX_Initialization(void)
{
  SPI_Initialization();
  SPI2_NSS=HIGH;
}

 /**
  * @brief  擦除FLASH扇区
  * @param  SectorAddr：要擦除的扇区地址
  * @retval 无
  */
void W25QXX_SectorEraseFun(u32 SectorAddr)
{
  /* 发送FLASH写使能命令 */
  W25QXX_WriteEnableFun();
  W25QXX_WaitForWriteEnd();
  /* 擦除扇区 */
  /* 选择FLASH: CS低电平 */
  SPI2_NSS=LOW;
  /* 发送扇区擦除指令*/
  W25QXX_SendByte(W25QXX_SectorErase);
  /*发送擦除扇区地址的高位*/
  W25QXX_SendByte((SectorAddr&0xFF0000)>>16);
  /* 发送擦除扇区地址的中位 */
  W25QXX_SendByte((SectorAddr&0xFF00)>>8);
  /* 发送擦除扇区地址的低位 */
  W25QXX_SendByte(SectorAddr&0xFF);
  /* 停止信号 FLASH: CS 高电平 */
  SPI2_NSS=HIGH;
  /* 等待擦除完毕*/
  W25QXX_WaitForWriteEnd();
}

 /**
  * @brief  擦除FLASH扇区，整片擦除
  * @param  无
  * @retval 无
  */
void W25QXX_AllErase(void)
{
  /* 发送FLASH写使能命令 */
  W25QXX_WriteEnableFun();

  /* 整块 Erase */
  /* 选择FLASH: CS低电平 */
  SPI2_NSS=LOW;
  /* 发送整块擦除指令*/
  W25QXX_SendByte(W25QXX_ChipErase);
  /* 停止信号 FLASH: CS 高电平 */
  SPI2_NSS=HIGH;

  /* 等待擦除完毕*/
  W25QXX_WaitForWriteEnd();
}

 /**
  * @brief  对FLASH按页写入数据，调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer，要写入数据的指针
  * @param WriteAddr，写入地址
  * @param  NumByteToWrite，写入数据长度，必须小于等于W25QXX_PerWritePageSize
  * @retval 无
  */
void W25QXX_PageWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
  /* 发送FLASH写使能命令 */
  W25QXX_WriteEnableFun();

  /* 选择FLASH: CS低电平 */
  SPI2_NSS=LOW;
  /* 写页写指令*/
  W25QXX_SendByte(W25QXX_PageProgram);
  /*发送写地址的高位*/
  W25QXX_SendByte((WriteAddr&0xFF0000)>>16);
  /*发送写地址的中位*/
  W25QXX_SendByte((WriteAddr&0xFF00)>>8);
  /*发送写地址的低位*/
  W25QXX_SendByte(WriteAddr&0xFF);

  if(NumByteToWrite>W25QXX_PerWritePageSize){
     NumByteToWrite=W25QXX_PerWritePageSize;
     FLASH_ERROR("W25QXX_PageWrite too large!"); 
  }

  /* 写入数据*/
  while(NumByteToWrite--){
    /* 发送当前要写入的字节数据 */
    W25QXX_SendByte(*pBuffer);
    /* 指向下一字节数据 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI2_NSS=HIGH;

  /* 等待写入完毕*/
  W25QXX_WaitForWriteEnd();
}

 /**
  * @brief  对FLASH写入数据，调用本函数写入数据前需要先擦除扇区
  * @param	pBuffer，要写入数据的指针
  * @param  WriteAddr，写入地址
  * @param  NumByteToWrite，写入数据长度
  * @retval 无
  */
void W25QXX_BufferWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
  u8 NumOfPage=0, NumOfSingle=0, Addr=0, count=0, temp=0;
	
	/*mod运算求余，若writeAddr是W25QXX_PageSize整数倍，运算结果Addr值为0*/
  Addr=WriteAddr%W25QXX_PageSize;
	
	/*差count个数据值，刚好可以对齐到页地址*/
  count=W25QXX_PageSize-Addr;
	/*计算出要写多少整数页*/
  NumOfPage=NumByteToWrite/W25QXX_PageSize;
	/*mod运算求余，计算出剩余不满一页的字节数*/
  NumOfSingle=NumByteToWrite%W25QXX_PageSize;
	
	/* Addr=0,则WriteAddr 刚好按页对齐 aligned  */
  if(Addr==0){
		/* NumByteToWrite < W25QXX_PageSize */
    if(NumOfPage==0){
      W25QXX_PageWrite(pBuffer,WriteAddr,NumByteToWrite);
    }else{ 
			/*先把整数页都写了*/
      while(NumOfPage--){
        W25QXX_PageWrite(pBuffer,WriteAddr,W25QXX_PageSize);
        WriteAddr+=W25QXX_PageSize;
        pBuffer+=W25QXX_PageSize;
      }
			/*若有多余的不满一页的数据，把它写完*/
      W25QXX_PageWrite(pBuffer,WriteAddr,NumOfSingle);
    }
  }else{
		/* NumByteToWrite < W25QXX_PageSize */
    if(NumOfPage==0){
			/*当前页剩余的count个位置比NumOfSingle小，一页写不完*/
      if(NumOfSingle>count){
        temp=NumOfSingle-count;
				/*先写满当前页*/
        W25QXX_PageWrite(pBuffer,WriteAddr,count);
        WriteAddr+=count;
        pBuffer+=count;
				/*再写剩余的数据*/
        W25QXX_PageWrite(pBuffer,WriteAddr,temp);
      }else{
        W25QXX_PageWrite(pBuffer,WriteAddr,NumByteToWrite);
      }
    }else{
			/*地址不对齐多出的count分开处理，不加入这个运算*/
      NumByteToWrite-=count;
      NumOfPage=NumByteToWrite/W25QXX_PageSize;
      NumOfSingle=NumByteToWrite%W25QXX_PageSize;
			
			/* 先写完count个数据，为的是让下一次要写的地址对齐 */
      W25QXX_PageWrite(pBuffer,WriteAddr,count);
			
			/* 接下来就重复地址对齐的情况 */
      WriteAddr+=count;
      pBuffer+=count;
			/*把整数页都写了*/
      while(NumOfPage--){
        W25QXX_PageWrite(pBuffer,WriteAddr,W25QXX_PageSize);
        WriteAddr+= W25QXX_PageSize;
        pBuffer+=W25QXX_PageSize;
      }
			/*若有多余的不满一页的数据，把它写完*/
      if(NumOfSingle!=0){
        W25QXX_PageWrite(pBuffer,WriteAddr,NumOfSingle);
      }
    }
  }
}

 /**
  * @brief  读取FLASH数据
  * @param 	pBuffer，存储读出数据的指针
  * @param   ReadAddr，读取地址
  * @param   NumByteToRead，读取数据长度
  * @retval 无
  */
void W25QXX_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)
{
  /* 选择FLASH: CS低电平 */
  SPI2_NSS=LOW;

  /* 发送 读 指令 */
  W25QXX_SendByte(W25QXX_ReadData);

  /* 发送 读 地址高位 */
  W25QXX_SendByte((ReadAddr&0xFF0000)>>16);
  /* 发送 读 地址中位 */
  W25QXX_SendByte((ReadAddr&0xFF00)>>8);
  /* 发送 读 地址低位 */
  W25QXX_SendByte(ReadAddr&0xFF);
	
	/* 读取数据 */
  while(NumByteToRead--){
    /* 读取一个字节*/
    *pBuffer=W25QXX_SendByte(Dummy_Byte);
    /* 指向下一个字节缓冲区 */
    pBuffer++;
  }

  /* 停止信号 FLASH: CS 高电平 */
  SPI2_NSS=HIGH;
}

 /**
  * @brief  读取FLASH ID
  * @param 	无
  * @retval FLASH ID
  */
u32 W25QXX_ReadID(void)
{
  u32 Temp=0;

  /* 开始通讯：CS低电平 */
  SPI2_NSS=LOW;

  /* 发送JEDEC指令，读取ID */
  W25QXX_SendByte(W25QXX_JedecDeviceID);

  /* 读取一个字节数据 */
  Temp|=((W25QXX_SendByte(Dummy_Byte))<<16);

  /* 读取一个字节数据 */
  Temp|=((W25QXX_SendByte(Dummy_Byte))<<8);

  /* 读取一个字节数据 */
  Temp|=W25QXX_SendByte(Dummy_Byte);

 /* 停止通讯：CS高电平 */
  SPI2_NSS=HIGH;

  return Temp;
}
 /**
  * @brief  读取FLASH Device ID
  * @param 	无
  * @retval FLASH Device ID
  */
u32 W25QXX_ReadDeviceID(void)
{
  u32 Temp=0;

  /* Select the FLASH: Chip Select low */
  SPI2_NSS=LOW;

  /* Send "RDID " instruction */
  W25QXX_SendByte(W25QXX_DeviceID);
  W25QXX_SendByte(Dummy_Byte);
  W25QXX_SendByte(Dummy_Byte);
  W25QXX_SendByte(Dummy_Byte);
  
  /* Read a byte from the FLASH */
  Temp=W25QXX_SendByte(Dummy_Byte);

  /* Deselect the FLASH: Chip Select high */
  SPI2_NSS=HIGH;

  return Temp;
}
/*******************************************************************************
* Function Name  : W25QXX_StartReadSequence
* Description    : Initiates a read data byte (READ) sequence from the Flash.
*                  This is done by driving the /CS line low to select the device,
*                  then the READ instruction is transmitted followed by 3 bytes
*                  address. This function exit and keep the /CS line low, so the
*                  Flash still being selected. With this technique the whole
*                  content of the Flash is read with a single READ instruction.
* Input          : - ReadAddr : FLASH's internal address to read from.
* Output         : None
* Return         : None
*******************************************************************************/
void W25QXX_StartReadSequence(u32 ReadAddr)
{
  /* Select the FLASH: Chip Select low */
  SPI2_NSS=LOW;

  /* Send "Read from Memory " instruction */
  W25QXX_SendByte(W25QXX_ReadData);

  /* Send the 24-bit address of the address to read from -----------------------*/
  /* Send ReadAddr high nibble address byte */
  W25QXX_SendByte((ReadAddr&0xFF0000)>>16);
  /* Send ReadAddr medium nibble address byte */
  W25QXX_SendByte((ReadAddr&0xFF00)>>8);
  /* Send ReadAddr low nibble address byte */
  W25QXX_SendByte(ReadAddr&0xFF);
}


 /**
  * @brief  使用SPI读取一个字节的数据
  * @param  无
  * @retval 返回接收到的数据
  */
u8 W25QXX_ReadByte(void)
{
  return (W25QXX_SendByte(Dummy_Byte));
}

 /**
  * @brief  使用SPI发送一个字节的数据
  * @param  byte：要发送的数据
  * @retval 返回接收到的数据
  */
u8 W25QXX_SendByte(u8 byte)
{
	 SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待发送缓冲区为空，TXE事件 */
  while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET){
    if((SPITimeout--)==0) return SPI_TIMEOUT_UserCallback(0);
  }

  /* 写入数据寄存器，把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(SPI2,byte);

	SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待接收缓冲区非空，RXNE事件 */
  while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET){
    if((SPITimeout--)==0) return SPI_TIMEOUT_UserCallback(1);
  }

  /* 读取数据寄存器，获取接收缓冲区数据 */
  return SPI_I2S_ReceiveData(SPI2 );
}

 /**
  * @brief  使用SPI发送两个字节的数据
  * @param  byte：要发送的数据
  * @retval 返回接收到的数据
  */
u16 W25QXX_SendHalfWord(u16 HalfWord)
{
  SPITimeout = SPIT_FLAG_TIMEOUT;
  /* 等待发送缓冲区为空，TXE事件 */
  while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET){
    if((SPITimeout--)==0) return SPI_TIMEOUT_UserCallback(2);
  }
	
  /* 写入数据寄存器，把要写入的数据写入发送缓冲区 */
  SPI_I2S_SendData(SPI2,HalfWord);

	 SPITimeout=SPIT_FLAG_TIMEOUT;
  /* 等待接收缓冲区非空，RXNE事件 */
  while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET){
    if((SPITimeout--)==0) return SPI_TIMEOUT_UserCallback(3);
  }
  /* 读取数据寄存器，获取接收缓冲区数据 */
  return SPI_I2S_ReceiveData(SPI2 );
}

 /**
  * @brief  向FLASH发送 写使能 命令
  * @param  none
  * @retval none
  */
void W25QXX_WriteEnableFun(void)
{
  /* 通讯开始：CS低 */
  SPI2_NSS=LOW;

  /* 发送写使能命令*/
  W25QXX_SendByte(W25QXX_WriteEnable);

  /*通讯结束：CS高 */
  SPI2_NSS=HIGH;
}

 /**
  * @brief  等待WIP(BUSY)标志被置0，即等待到FLASH内部数据写入完毕
  * @param  none
  * @retval none
  */
void W25QXX_WaitForWriteEnd(void)
{
  u8 FLASH_Status=0;

  /* 选择 FLASH: CS 低 */
  SPI2_NSS=LOW;

  /* 发送 读状态寄存器 命令 */
  W25QXX_SendByte(W25QXX_ReadStatusReg);

  /* 若FLASH忙碌，则等待 */
  do{
		/* 读取FLASH芯片的状态寄存器 */
    FLASH_Status=W25QXX_SendByte(Dummy_Byte);	 
  }while ((FLASH_Status&WIP_Flag)==SET);  /* 正在写入标志 */

  /* 停止信号  FLASH: CS 高 */
  SPI2_NSS=HIGH;
}


//进入掉电模式
void W25QXX_PowerDownFun(void)   
{ 
  /* 通讯开始：CS低 */
  SPI2_NSS=LOW;

  /* 发送 掉电 命令 */
  W25QXX_SendByte(W25QXX_PowerDown);

  /*通讯结束：CS高 */
  SPI2_NSS=HIGH;
}   

//唤醒
void W25QXX_WAKEUP(void)   
{
  /*选择 FLASH: CS 低 */
  SPI2_NSS=LOW;

  /* 发送 上电 命令 */
  W25QXX_SendByte(W25QXX_ReleasePowerDown);

   /* 停止信号 FLASH: CS 高 */
  SPI2_NSS=HIGH;
}   
   

/**
  * @brief  等待超时回调函数
  * @param  None.
  * @retval None.
  */
static  uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
  /* 等待超时后的处理,输出错误信息 */
  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);
  return 0;
}